/*
 * Copyright 2018 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.paboo.chart.qrcode;

import com.d_project.qrcode.QRCode;
import com.google.gson.Gson;
import com.google.gson.JsonIOException;
import com.google.gson.JsonSyntaxException;
import org.paboo.codec.Base64;
import org.paboo.utils.LoggerFactoryUtils;
import org.paboo.utils.ParameterObject;
import org.paboo.utils.ResponseUtils;
import org.paboo.utils.ZxingImage;
import org.springframework.hateoas.ExposesResourceFor;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;

/**
 * @author Leonard
 */
@RestController
@ExposesResourceFor(QrdEntity.class)
public class QrdController {

    private static final long serialVersionUID = 6490130504990685386L;
    private LoggerFactoryUtils logger = LoggerFactoryUtils.getInstance().load(QrdController.class);


    @RequestMapping("/qrd")
    public ResponseEntity<byte[]> handleControllerExecution(HttpMethod method,
                                                            @RequestParam(value = "data", required = false, defaultValue = ParameterObject.DEFAULT_CONTECT) String ctx,
                                                            @RequestParam(value = "type", required = false, defaultValue = "0") int type,
                                                            @RequestParam(value = "size", required = false, defaultValue = "1") int size,
                                                            @RequestParam(value = "margin", required = false, defaultValue = "2") int margin,
                                                            @RequestParam(value = "level", required = false, defaultValue = "H") String level,
                                                            @RequestParam(value = "format", required = false, defaultValue = "gif") String format,
                                                            @RequestBody(required = false) String requestStr) throws IOException {

        MediaType mt = MediaType.TEXT_PLAIN;
        QrdEntity e = new QrdEntity();
        Gson gson = new Gson();

        if (!ParameterObject.hasText(requestStr)) {
            requestStr = "{}";
        }

        if (method == HttpMethod.GET) {

            if ((type < 0 || 10 < type)
                    || (margin < 0 || 32 < margin)
                    || (size < 0 || 32 < size)) {
                String errorMsg = "illegal number";

                return ResponseUtils.sendErrorOverJSON(HttpStatus.BAD_REQUEST, errorMsg);
            }

            String encoding = StandardCharsets.UTF_8.name();
            e.setData(URLDecoder.decode(ctx, encoding));
            e.setLevel(level);
            e.setType(type);
            e.setSize(size);
            e.setMargin(margin);
            e.setFormat(format);
            e.setNeedBase64(false);
        } else if (method == HttpMethod.POST) {
            try {
                e = gson.fromJson(requestStr, QrdEntity.class);
            } catch (JsonIOException | JsonSyntaxException ex) {
                String errorMsg = "Data parameter ERROR!";
                logger.error(errorMsg);
                return ResponseUtils.sendErrorOverJSON(HttpStatus.BAD_REQUEST, errorMsg);
            }

            if (e == null) {
                e = new QrdEntity();
            }
            if (!ParameterObject.hasText(e.getData())) {
                e.setData(ctx);
            }
            if (!ParameterObject.hasText(e.getLevel())) {
                e.setLevel(level);
            }
            if (!ParameterObject.hasInteger(e.getType())) {
                e.setType(type);
            }
            if (!ParameterObject.hasInteger(e.getSize())) {
                e.setSize(size);
            }
            if (!ParameterObject.hasInteger(e.getMargin())) {
                e.setMargin(margin);
            }
            if (!ParameterObject.hasText(e.getFormat())) {
                e.setFormat(format);
            }
            if (!ParameterObject.hasBoolean(e.getNeedBase64())) {
                e.setNeedBase64(false);
            }
        } else {
            String errorMsg = "Request error!!!";
            logger.error(errorMsg);
            return ResponseUtils.sendErrorOverJSON(HttpStatus.NOT_IMPLEMENTED, "Request error!!!");
        }

        logger.info(gson.toJson(e));

        if ("text".equalsIgnoreCase(format)) {

            String outMsg;
            try {
                outMsg = ParameterObject.convertByteArrays(QRdUtil.getQRCodeBytes(e));
            } catch (IllegalArgumentException ex) {
                outMsg = "Create Fail!!!";
                if (logger.isDebugEnabled()) {
                    logger.error(outMsg, ex);
                } else {
                    logger.error(outMsg);
                }
            }

            return ResponseUtils.sendOkContent(mt, outMsg.getBytes());
        } else if ("svg".equalsIgnoreCase(format)) {
            mt = MediaType.parseMediaType("image/svg+xml");
            ByteArrayOutputStream out = new ByteArrayOutputStream();

            QRCode qrcode = QRdUtil.getQRCode(e);
            int imageSize = qrcode.getModuleCount() * e.getSize() + e.getMargin() * 2;
            ZxingImage.toSVGDocument(
                    ParameterObject.convertToZxingByteMatrix(qrcode),
                    new OutputStreamWriter(new BufferedOutputStream(out), StandardCharsets.ISO_8859_1.name()),
                    imageSize, imageSize, e.getMargin(), ZxingImage.BLACK, ZxingImage.WHITE);


            return ResponseUtils.sendOkContent(mt, out.toByteArray());
        }


        ByteArrayOutputStream out = new ByteArrayOutputStream();
        BufferedImage image = QRdUtil.getQRCodeImage(e);
        if (image == null || !ImageIO.write(image, e.getFormat().toLowerCase(), out)) {
            String errorMsg = "Could not write an image of format " + e.getFormat().toLowerCase();
            logger.error(errorMsg);
            return ResponseUtils.sendErrorOverJSON(HttpStatus.INTERNAL_SERVER_ERROR, errorMsg);
        }

        if(e.getNeedBase64()) {
            String base64 = Base64.encodeToString(out.toByteArray());
            return ResponseUtils.sendOkContent(mt, base64.getBytes());
        }

        if (e.getFormat().equalsIgnoreCase("png")) {
            mt = MediaType.IMAGE_PNG;
        } else if (e.getFormat().equalsIgnoreCase("gif")) {
            mt = MediaType.IMAGE_GIF;
        } else if (e.getFormat().equalsIgnoreCase("jpg") || e.getFormat().equalsIgnoreCase("jpeg")) {
            mt = MediaType.IMAGE_JPEG;
        } else {
            return ResponseUtils.sendErrorOverJSON(HttpStatus.NOT_IMPLEMENTED, "Not implement image");
        }

        return ResponseUtils.sendOkContent(mt, out.toByteArray());
    }
}
